package com.modernyuanshiren;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Solution<T extends IStatus<T, U>, U extends ISingleAction<T, U>> {
	private List<T> statuses = new ArrayList<>();
	private List<IAction<T, U>> actions = new ArrayList<>();

	public Solution(T initStatus) {
		super();
		statuses.add(initStatus);
	}

	public Solution(Solution<T, U> parent, IAction<T, U> action) {
		super();
		statuses.addAll(parent.statuses);
		actions.addAll(parent.actions);
		statuses.add(action.apply(parent.getCurrentStatus()));
		actions.add(action);
	}

	public List<Solution<T, U>> nextLevel() {
		return getCurrentStatus()
				.getAvailableActions()
				.stream()
				.filter(action -> actions.isEmpty()
						|| !action.notNecessary(getPreAction()))
				.map(action -> new Solution<T, U>(this, action))
				.collect(Collectors.toList());
	}

	private T getCurrentStatus() {
		return statuses.get(statuses.size() - 1);
	}

	private IAction<T, U> getPreAction() {
		return actions.get(actions.size() - 1);
	}

	public boolean isEnd() {
		return getCurrentStatus().isEnd();
	}

	public void output() {
//		int lengthOfTotalSteps = String.valueOf(actions.size()).length();
//		IntStream.range(0, actions.size()).forEach(
//				i -> {
//					System.out.printf(
//							"Step %" + lengthOfTotalSteps + "d: %s\n", i + 1,
//							actions.get(i).toString());
//				});
		packOutput();
	}

	private void packOutput() {
		List<String> packedNames = new ArrayList<>();
		List<Integer> repeatCounts = new ArrayList<>();
		List<String> allNames = actions.stream().map(IAction::toString)
				.collect(Collectors.toList());
		IntStream
				.range(0, allNames.size())
				.forEach(
						i -> {
							if (i == 0
									|| !allNames.get(i).equals(
											allNames.get(i - 1))) {
								packedNames.add(allNames.get(i));
								repeatCounts.add(1);
							} else {
								repeatCounts.set(
										repeatCounts.size() - 1,
										repeatCounts.get(repeatCounts.size() - 1) + 1);
							}
						});
		IntStream.range(0, packedNames.size()).forEach(
				i -> {
					System.out.print("Step ");
					final int from = repeatCounts.stream().limit(i)
							.mapToInt(Integer::intValue).sum() + 1;
					StringBuffer sbf = new StringBuffer();
					sbf.append(from);
					final int to = repeatCounts.stream().limit(i + 1)
							.mapToInt(Integer::intValue).sum();
					if (to > from) {
						sbf.append("-");
						sbf.append(to);
						System.out.printf("%5s: %s(%d次)\n", sbf.toString(),packedNames.get(i),
								repeatCounts.get(i));
					} else {
						System.out.printf("%5s: %s\n", sbf.toString(),packedNames.get(i));
					}
				});
	}

	public List<IAction<T, U>> getActions() {
		return actions;
	}
	
	public boolean checkSymmetry(Solution<T,U> other) {
		return this.getCurrentStatus().checkSymmetry(other.getCurrentStatus());
	}
	
	public boolean isSymmetry() {
		return this.getCurrentStatus().isSymmetry();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime
				* result
				+ ((getCurrentStatus() == null) ? 0 : getCurrentStatus()
						.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Solution other = (Solution) obj;
		if (getCurrentStatus() == null) {
			if (other.getCurrentStatus() != null)
				return false;
		} else if (!getCurrentStatus().equals(other.getCurrentStatus()))
			return false;
		return true;
	}

}
